/*
 * Copyright (c) 2021 HiSilicon (Shanghai) Technologies CO., LIMITED.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>

#include "histream.h"
#include "hi_ext_util.h"
#include "mpp_help.h"
#include "aic_mng.h"
#include "uvc_strm.h"
#include "uvc_def.h"
#include "uvc_aic.h"

/**
    UVC Agent of AIC.
*/
struct UvcAgent {
    uvc_dev_ext_t *uvcDev; // uvc device used

    // aic stream相关
    PAYLOAD_TYPE_E videoCodec;
    int videoWidth;
    int videoHeight;
    int vencChn;

    // uvc stream相关
    bool uvcWorking; // UVC stream是否已经started，由uvc_dev引发
    bool strmMatch; // UVC stream需求是否与aic stream匹配，即格式像素等是否一致
    encoder_property uvcProp; // uvc提供的prop

    bool inInitTx; // 正在发送初始图片
};

/**
    因uvc_dev实现的限制，暂时采用全局对象. 将来应修改为可多个UvcAgent实例共存.
*/
static UvcAgent *g_uaic = NULL;

/**
    将UVC的像素格式转换为MPP的.
*/
static HI_S32 PixfmtUvcToMpp(unsigned int uvcFmt)
{
    if (uvcFmt == V4L2_PIX_FMT_YUYV) {
        return PIXEL_FORMAT_YVU_SEMIPLANAR_422;
    } else {
        return PIXEL_FORMAT_YVU_SEMIPLANAR_420;
    }
}

/**
    将UVC的codec type转换为MPP的(payload type).
*/
static PAYLOAD_TYPE_E CodecUvcToMpp(uint32_t fcc)
{
    PAYLOAD_TYPE_E t;

    switch (fcc) {
        case V4L2_PIX_FMT_MJPEG:
            t = PT_MJPEG;
            break;

        case V4L2_PIX_FMT_H264:
            t = PT_H264;
            break;

        case V4L2_PIX_FMT_YUYV:
        default:
            t = PT_MJPEG;
            break;
    }
    return t;
}

/**
    判断UVC的stream需求与AIC的stream输出是否匹配.
*/
static void ChkStrmMatch(UvcAgent* self)
{
    if (!self->uvcWorking || self->videoCodec == PT_BUTT) {
        return;
    }

    encoder_property *uvcProp = &self->uvcProp;
    PAYLOAD_TYPE_E codecType = CodecUvcToMpp(uvcProp->format);

    self->strmMatch = (codecType == self->videoCodec &&
        uvcProp->width == self->videoWidth && uvcProp->height == self->videoHeight);

    // H.264/H.265才发出IDR请求
    if (self->strmMatch && (self->videoCodec == PT_H264 || self->videoCodec == PT_H265)) {
        LOGW("set IDR\n");
        int ret = HI_MPI_VENC_RequestIDR(self->vencChn, HI_TRUE);
        HI_EXP_LOGE(ret, "HI_MPI_VENC_RequestIDR FAIL, ret=%#x\n", ret);
    }
}

/**
    当UVC strm被host停止.
*/
static int OnUvcStopStrm(void)
{
    LOGI("UVC-AIC: on UVC strm stop ...\n");
    bool ret = IsMainThrd();
    HI_ASSERT(ret);
    HI_ASSERT(g_uaic);
    UvcAgent *self = g_uaic;

    AicStopVo(AIC_VOT_UVC);
    AicStopVi(AIC_VIT_SENSOR, false);

    self->uvcWorking = false;
    self->strmMatch = false;

    LOGI("UVC-AIC: on UVC strm stop done\n");
    return 0;
}

/**
    当UVC strm被host启动.
*/
static int OnUvcStartStrm(void)
{
    LOGI("UVC-AIC: on UVC strm start ...\n");
    bool res = IsMainThrd();
    HI_ASSERT(res);
    HI_ASSERT(g_uaic);
    UvcAgent *self = g_uaic;
    int ret;

    encoder_property *uvcProp = &self->uvcProp;
    PIXEL_FORMAT_E pixFormat = PixfmtUvcToMpp(uvcProp->format);
    PAYLOAD_TYPE_E codecType = CodecUvcToMpp(uvcProp->format);
    uint32_t width = self->uvcProp.width;
    uint32_t height = self->uvcProp.height;

    LOGI("UVC-AIC: start UVC strm, codec=%d, size={%ux%u} ...\n", codecType, width, height);

    // 暂时不支持者两种格式
    if (uvcProp->format == V4L2_PIX_FMT_YUYV || uvcProp->format == V4L2_PIX_FMT_YUV420) {
        LOGE("UVC-AIC: not supp YUYV, YUV420 now\n");
        return -1;
    }

    // 先启动VO为宜
    ret = AicStartVo(AIC_VOT_UVC, width, height, codecType);
    HI_EXP_RET(ret < 0, ret, "start VOT_UVC FAIL, ret=%d\n", ret);

    ret = AicStartVi(AIC_VIT_SENSOR, false, width, height, pixFormat);
    if (ret < 0) {
        LOGE("start VIT_SENSOR FAIL, ret=%d\n", ret);
        AicStopVo(AIC_VOT_UVC);
        return ret;
    }

    self->uvcWorking = true;
    ChkStrmMatch(self);

    // 当aic实际viType为HOST时，则发送initPic
    if (self->strmMatch && AicGetViType() == AIC_VIT_HOST &&
        (ret = AicTxInitPic()) >= 0) {
        self->inInitTx = true;
    }

    LOGI("UVC-AIC: on UVC strm start done\n");
    return ret;
}

/**
    当UVC请求获取IDR frame.
*/
static HI_S32 OnUvcSetIdr(void)
{
    LOGD("UVC-AIC: on UVC set IDR ...\n");
    bool res = IsMainThrd();
    HI_ASSERT(res);
    HI_ASSERT(g_uaic);
    UvcAgent *self = g_uaic;

    if (self->uvcWorking && self->vencChn >= 0 &&
        (self->videoCodec == PT_H264 || self->videoCodec == PT_H265)) {
        LOGI("UVC-AIC: setIdr\n");
        int ret = HI_MPI_VENC_RequestIDR(g_uaic->vencChn, HI_TRUE);
        HI_EXP_LOGE(ret, "HI_MPI_VENC_RequestIDR FAIL, ret=%#x\n", ret);
        return ret;
    } else {
        LOGI("UVC-AIC: setIdr ignore, for vencChn=%d, codecType=%d\n", self->vencChn, self->videoCodec);
        return 0;
    }
}

/**
    UVC设置strm的属性.
*/
static HI_S32 OnUvcSetProp(encoder_property *p)
{
    LOGD("UVC-AIC: on UVC set prop ...\n");
    bool ret = IsMainThrd();
    HI_ASSERT(ret);
    HI_ASSERT(g_uaic);
    UvcAgent *self = g_uaic;

    self->uvcProp = *p;
    return 0;
}

/**
    提供给UVC的回调接口集.
*/
static struct stream_control_ops g_uvcScOps = {
    .init = NULL,
    .startup = OnUvcStartStrm,
    .shutdown = OnUvcStopStrm,
    .set_idr = OnUvcSetIdr,
    .set_property = OnUvcSetProp,
};

/**
    AicMng引发strm off.
*/
static void OnAicStrmOff(void* user)
{
    LOGI("SAVER: on strm off\n");
    HI_ASSERT(user);
    UvcAgent *self = (UvcAgent*)user;

    self->vencChn = -1;
    self->videoWidth = 0;
    self->videoHeight = 0;
    self->videoCodec = PT_BUTT;

    self->strmMatch = false;
}

/**
    AicMng引发strm on.
*/
static bool OnAicStrmOn(void* user, int vencChn, PAYLOAD_TYPE_E codecType, int width, int height)
{
    LOGI("UVC-AIC: on strm on, codec=%d, width=%d, height=%d\n", codecType, width, height);
    HI_ASSERT(user);
    UvcAgent *self = (UvcAgent*)user;

    if (self->videoCodec != PT_BUTT) {
        LOGW("onStrmOn without onStrmOff before\n");
        OnAicStrmOff(user);
    }

    self->vencChn = vencChn;
    HI_ASSERT(width > 0 && height > 0);
    self->videoWidth = width;
    self->videoHeight = height;
    self->videoCodec = codecType;

    ChkStrmMatch(self);
    return true;
}

/**
    AicMng引发strm frm.
*/
static void OnAicVencFrm(void* user, VencFrm* frm)
{
    HI_ASSERT(user && frm);
    UvcAgent *self = (UvcAgent*)user;
    int ret;

    if (!self->uvcWorking) {
        return;
    }
    if (!self->strmMatch) {
        LOGD("discard aic frm, for strm not match\n");
        return;
    }

    MemBlk *mblk = uvc_alloc_buf(self->uvcDev);
    if (!mblk) {
        return;
    }

    VencFrmToMblk(&frm->strm, mblk, 0);
    ret = uvc_tx_frm(self->uvcDev, mblk);
    HI_EXP_LOGE(ret < 0, "tx frm to UVC FAIL, ret=%d\n", ret);

    // 这里只判断inInitTx，而不再判断aic实际vitype
    if (self->inInitTx) {
        self->inInitTx = false;
        if (!uvc_strm_ready(self->uvcDev) && (ret = AicTxInitPic()) >= 0) {
            self->inInitTx = true;
        }
    }
}

/**
    实现的AicMng StrmUser接口.
*/
static const IAicStrmUser G_UVC_STRM_USER = {
    .OnStrmOn = OnAicStrmOn,
    .OnStrmOff = OnAicStrmOff,
    .OnVencFrm = OnAicVencFrm,
    .OnVideoFrm = NULL,
};

/**
    创建并启动UVC AIC agent.
*/
int UvcAgentCreate(UvcAgent** agent, uvc_dev_ext_t* uvcDev)
{
    HI_ASSERT(!g_uaic);
    HI_ASSERT(agent && uvcDev);
    *agent = NULL;
    int ret;

    UvcAgent *self = (UvcAgent*)malloc(sizeof(*self));
    HI_ASSERT(self);
    if (memset_s(self, sizeof(*self), 0, sizeof(*self)) != EOK) {
        HI_ASSERT(0);
    }

    self->vencChn = -1;
    self->videoWidth = 0;
    self->videoHeight = 0;
    self->videoCodec = PT_BUTT;
    self->uvcWorking = false;
    self->strmMatch = false;
    self->inInitTx = false;

    self->uvcDev = uvcDev;
    histream_register_mpi_ops_ext(&g_uvcScOps, &g_strmIspPuOps, &g_strmIspItOps, NULL);

    ret = (int)AicAddStrmUser(&G_UVC_STRM_USER, self);
    HI_ASSERT(ret);

    *agent = g_uaic = self;
    LOGI("UVC-AIC: created\n");
    return 0;
}

/**
    终止并销毁UVC AIC agent.
*/
void UvcAgentDestroy(UvcAgent* self)
{
    HI_ASSERT(g_uaic);
    HI_ASSERT(self && self == g_uaic);
    int ret;

    ret = (int)AicDelStrmUser(&G_UVC_STRM_USER, self);
    HI_ASSERT(ret);

    histream_register_mpi_ops_ext(NULL, NULL, NULL, NULL);
    HI_ASSERT(self->uvcDev);

    free(self);
    g_uaic = NULL;
    LOGI("UVC-AIC: destroyed\n");
}

